跳到主要内容

Java IO学习-字符流

IO流工具类一览

1725831-c4c249286ae6e718

一切文件数据(文本、图片、视频等)在存储时,都是可以通过字节流进行操作

字符流抽象类

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以 Java 提供一些字符流,以字符为单位读写数据,专门用于处理文本文件。

同上面的字节流,字符流也搞了两个抽象类 Reader、Writer

  • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
  • FileReader:读取文件的字符流
  • FilterReader:用于读取过滤后的字符流
  • PipedReader:字符管道流
  • InputStreamReader:是从字节流到字符流的“桥”:它读取字节,并使用指定的charset将其解码为字符 。

为了最大的效率,一般在 BufferedReader 中包装一个 InputStreamReader

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

字符编码

计算机中储存的信息都是用二进制数表示的,而屏幕上的信息都是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为 编码。反之,将存储在计算机中的二进制数按照某种规则显示出来,称为 解码

编码:字符(能看懂的)--->字节(看不懂的) 解码:字节(看不懂的)--->字符(能看懂的)

  • 字符编码 character Encoding 就是一套自然语言的字符与二进制数之间的对应规则
  • 编码表:生活中文字和计算机中二进制对应的规则

字符集

字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国文字,标点符号,图形符号,数字等;计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见的字符集有

  • ASCII字符集
  • GBK字符集
  • Unicode字符集
    • UTF-8
    • UTF-16
    • UTF-32

而 FileReader 默认的编码格式是 UTF-8

Reader 抽象类

//关闭流并释放与之相关联的任何系统资源。  
void close()
//标记流中的当前位置。
void mark(int readAheadLimit)
//告诉这个流是否支持mark()操作。
boolean markSupported()
//读一个字符
int read()
//将字符读入数组的一部分。
int read(char[] cbuf, int off, int len)
//告诉这个流是否准备好被读取。
boolean ready()
//将流重新设置为最近的标记,如果从未被标记,则将其重置到字符串的开头。
void reset()
//跳过流中指定数量的字符。
long skip(long ns)

Writer 抽象类

// 将指定字符添加到此 writer
Writer append(char c)
// 将指定字符序列添加到此 writer
Writer append(CharSequence csq)
// 将指定字符序列的子序列添加到此 writer.Appendable
Writer append(CharSequence csq, int start, int end)
// 关闭此流,但会先刷新它
abstract void close()
// 刷新该流的缓冲
abstract void flush()
// 写入字符数组
void write(char[] cbuf)
// 写入字符数组的某一部分
abstract void write(char[] cbuf, int off, int len)
// 写入单个字符
void write(int c)
// 写入字符串
void write(String str)
// 写入字符串的某一部分
void write(String str, int off, int len)

文件字符流

FileReader 读取文件

public class learn01 {
public static void main(String[] args) throws IOException {
String name = "a.txt";
String path = "C:\\Users\\alsritter\\Desktop";
File file = new File(path, name);

//用来测试程序的效率,所以读取前后时间
long s = System.currentTimeMillis();

FileReader fileReader = new FileReader(file);
char[] buffer = new char[1024];

int len;
while ((len = fileReader.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
System.out.println("**************");
}
fileReader.close();

// 返回当前毫秒
long e = System.currentTimeMillis();
System.out.println("共耗时:" + (e - s) + "毫秒");
}
}

FileWriter 写入文件

使用步骤:

  1. 创建 FileWriter 对象,构造方法中绑定要写入数据的目的地(也可以设置为追加写入)
  2. 使用 FileWriter 中的方法 write,把数据写入到内存缓冲区中去(字符转换为字节的过程)
  3. 使用 flush 方法把缓存区的数据刷新到文件中
  4. close 释放资源(会先把内存缓冲区中的数据刷新到文件中,再释放资源)

Java 在使用流时,都会有一个缓冲区,按一种它认为比较高效的方法来发数据:把要发的数据先放到缓冲区,缓冲区放满以后再一次性发过去,而不是分开一次一次地发;而 flush() 表示强制将缓冲区中的数据发送出去,不必等到缓冲区满。

public class learn01 {
public static void main(String[] args) throws IOException {
String name = "a.txt";
String path = "C:\\Users\\alsritter\\Desktop";
File file = new File(path,name);
File copyFile = new File(path, "copy_" + file.getName());

//用来测试程序的效率,所以读取前后时间
long s = System.currentTimeMillis();

FileReader fileReader = new FileReader(file);
FileWriter fileWriter = new FileWriter(copyFile,true);

char[] buffer = new char[1024];
int len;
while ((len=fileReader.read(buffer))!=-1){
fileWriter.write(buffer,0,len);
}
fileWriter.close();
fileReader.close();

// 返回当前毫秒
long e = System.currentTimeMillis();
System.out.println("共耗时:"+(e - s)+"毫秒");
}
}

字符缓冲流

按照数据类型分类:

  • 字节缓冲流:BufferedInputStreamBufferedOutputStream
  • 字符缓冲流:BufferedReaderBufferedWriter

缓冲流的基本原理就是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率,更多细节看字节流那里的字节缓冲流

BufferedReader

BufferedReader 能为字符输入流提供缓冲区,可以提高许多 IO 处理的速度。可以一次读取一大块的数据,而不需要每次从网络或者磁盘中一次读取一个字节。特别是在访问大量磁盘数据时,缓冲通常会让 IO 快上许多。

构造函数

/* 
* 参数:
* in - 一个 Reader
* sz - 输入缓冲区的大小
* 如果 sz <= 0 会抛出:IllegalArgumentException
*/
public BufferedReader(Reader in,int sz) //创建一个使用指定大小输入缓冲区的缓冲字符输入流。

public BufferedReader(Reader in) // 创建一个使用默认大小输入缓冲区的缓冲字符输入流。

常用方法

// read 读取单个字符。如果已到达流末尾,则返回 -1
public int read() throws IOException

// readLine 读取一个文本行。通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。
public String readLine() throws IOException

// close 关闭该流病释放与之关联的所有资源。

使用例

public class Temp {
public static void main(String[] args) {
// 声明 BufferedReader 对象
BufferedReader buf = null;

//实例化BufferedReader对象
buf = new BufferedReader(new InputStreamReader(System.in));
// 接收输入内容
String str = null;
System.out.print("请输入内容:");
try {
// 读取输入内容
str = buf.readLine();
} catch (IOException e) {
e.printStackTrace();
}

System.out.println("输入的内容:" + str);
}
}

BufferedWriter

BufferedWriter 是缓冲字符输出流。它继承于Writer。它的作用是为其他字符输出流添加一些缓冲功能,能够提高效率。

构造函数

BufferedWriter(Writer out)             // 使用默认cb大小创建BufferedWriter。  
BufferedWriter(Writer out, int sz) // 使用默认cb大小创建BufferedWriter。

内部关键字

private Writer out;                 // 底层字符输出流  
private char cb[]; // 缓冲数组
private int nChars, nextChar; // nChars--cb的size,nextChar--cb中下一个字符的下标
private static int defaultCharBufferSize = 8192; // 默认cb大小
private String lineSeparator; // 换行符、用于newLine方法。不同平台具有不同的值。

常用方法

void close() // 关闭此流、释放与此流有关的资源。  
void flushBuffer() // 将cb中缓存的字符flush到底层out中
void flush() // 刷新此流、同时刷新底层out流
void newLine() // 写入一个换行符。
void write(int c) // 将一个单个字符写入到cb中。
void write(char cbuf[], int off, int len) // 将一个从下标off开始长度为len个字符写入cb中
void write(String s, int off, int len) // 将一个字符串的一部分写入cb中

使用方法

public static void main(String[] args) throws IOException {
//找到目标文件
File file = new File("F:\\a.txt");
//建立数据的输出通道
FileWriter fileWriter = new FileWriter(file,true);
//建立缓冲输出流对象
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//写出数据
// bufferedWriter.newLine(); //newLine() 换行。 实际上就是想文件输出\r\n.
bufferedWriter.write("\r\n");
bufferedWriter.write("hello world!!");
//关闭资源
bufferedWriter.flush();
// bufferedWriter.close();

}

字节流与字符流之间的桥梁

InputStreamReader、OutputStreamWriter 是字节流和字符流之间的桥梁。它读写字节,并使用指定的字符集将其解码为字符。一般用作从网络流中读取文本

一般将 InputStreamReader 包裹在 BufferedReader 中以获得最佳效率

请注意,在 Java 中使用字符流时,应避免使用依赖于默认编码的流,例如 FileReader 或 PrintWriter。

//构造方法来指定编码
InputStreamReader(InputStream in, String charsetName)
OutputStreamWriter(OutputStream out, String charsetName)

InputStreamReader

InputStreamReader 将字节流转换为字符流。是字节流通向字符流的桥梁。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。

构造函数

InputStreamReader(InputStream in)   //创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in, Charset cs) //创建使用给定字符集的 InputStreamReader。
InputStreamReader(InputStream in, CharsetDecoder dec) //创建使用给定字符集解码器的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName) //创建使用指定字符集的 InputStreamReader。

常用方法

void close()                                      //关闭该流并释放与之关联的所有资源。
String getEncoding() // 返回此流使用的字符编码的名称。
int read() //读取单个字符。
int read(char[] cbuf, int offset, int length) // 将字符读入数组中的某一部分。
String readLine() // 读取一行
boolean ready() //判断此流是否已经准备好用于读取。

使用例:这里使用 InputStreamReader 从字节流里面读取文本

public class Temp {
public static void main(String[] args) throws IOException {

URL resource = Temp.class.getClassLoader().getResource("./temp.txt");
// file:/D:/JavaProject/study-java/studySocket/target/classes/temp.txt

try (FileInputStream fis = new FileInputStream(resource.getPath());
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr)) {

String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
}

从网络流里读取文本

public class Temp {

public static void main(String[] args) throws MalformedURLException, IOException {

StringBuilder sb;

URL url = new URL("http://www.something.com");

try (InputStreamReader isr = new InputStreamReader(url.openStream(),
StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr)) {

String line;

sb = new StringBuilder();

while ((line = br.readLine()) != null) {

sb.append(line);
sb.append(System.lineSeparator());
}
}

System.out.println(sb.toString());
}
}

OutputStreamWriter

Java 中 OutputStreamWriter 是从字符流到字节流的桥接,自动将要写入流中的字符编码成字节,等于 FileOutputStream + 编码表,而 OutputStreamWriter.write() 都会导致在给定字符(或字符集)上调用编码转换器,直接写出 UTF-8 编码后的字符。

public void write(int c)                            // 写一个字符
public void write(char[] cbuf) // 写一个字符数组
public void write(char[] cbuf,int off,int len) // 写一个字符数组的一部分
public void write(String str) // 写一个字符串
public void write(String str,int off,int len) // 写一个字符串的一部分

使用还是挺简单的

// 创建对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("fos.txt"),"UTF-8");

// 写一个字符
osw.write('a');
osw.write(98);
osw.write("\r\n");

//写一个字符数组
char[] chs = {'q','w','e','r','t'};
osw.write(chs);
osw.write("\r\n");

//写一个字符数组的一部分
osw.write(chs,1,3);
osw.write("\r\n");

//写一个字符串
osw.write("www.51gjie.com");
osw.write("\r\n");

//写一个字符串的一部分
osw.write("hello world", 2, 3);
osw.write("\r\n");

// 刷新缓冲区
osw.flush();
// 释放资源
osw.close();

文件内字符替换

读取文件代码如下:

File file = new File("C:/Users/Administrator/Desktop/test1.json");
try {
String content = FileUtils.readFileToString(file, "utf-8");
System.out.println(content);
} catch (Exception e) {
e.printStackTrace();
}

缺点:要导入 commons-io-2.4.jar 文件

替换文本内的内容:


/**
* 替换文本文件中的 非法字符串
* @param path
* @throws IOException
*/
public void replacTextContent(String path) throws IOException{
//原有的内容
String srcStr = "name:";
//要替换的内容
String replaceStr = "userName:";
// 读
File file = new File(path);
FileReader in = new FileReader(file);
BufferedReader bufIn = new BufferedReader(in);
// 内存流, 作为临时流
CharArrayWriter tempStream = new CharArrayWriter();
// 替换
String line = null;

while ( (line = bufIn.readLine()) != null) {
// 替换每行中, 符合条件的字符串
line = line.replaceAll(srcStr, replaceStr);
// 将该行写入内存
tempStream.write(line);
// 添加换行符
tempStream.append(System.getProperty("line.separator"));
}
// 关闭 输入流
bufIn.close();
// 将内存中的流 写入 文件
FileWriter out = new FileWriter(file);
tempStream.writeTo(out);
out.close();
System.out.println("====path:"+path);

}